package com.vvv.designpattern.service;

import com.vvv.designpattern.constants.RedisConstants;
import com.vvv.designpattern.items.composite.AbstractProductItem;
import com.vvv.designpattern.items.composite.ProductComposite;
import com.vvv.designpattern.items.vistor.AddItemVisitor;
import com.vvv.designpattern.items.vistor.DelItemVisitor;
import com.vvv.designpattern.pojo.ProductItem;
import com.vvv.designpattern.repo.ProductItemRepository;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.vvv.designpattern.constants.RedisConstants.CACHE_PRODUCT_COMPOSITE_KEY;

/**
 * @author vvv
 * @date 2023-11-18 15 45
 * discription
 */
@Service
@Transactional // jpa需要
public class ProductItemService {

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ProductItemRepository productItemRepository;

    @Resource
    private AddItemVisitor addItemVisitor;

    @Resource
    private DelItemVisitor delItemVisitor;

    /**
     * 添加商品类目
     * @param item
     * @return
     */
    public ProductComposite addItems(ProductItem item) {
        // 1. 先更新数据库
        productItemRepository.addItem(item.getName(), item.getPid());
        // 2. item --> productComposite
        ProductComposite addItem = ProductComposite.builder()
                .id(productItemRepository.findByNameAndPid(item.getName(), item.getPid()).getId())
                .name(item.getName())
                .pid(item.getPid())
                .child(new ArrayList<>())
                .build();
        // 3. 通过addItemVisitor添加节点
        AbstractProductItem updatedItems = addItemVisitor.visitor(addItem);
        // 4. 更新redis
        // TODO 此处可以做重试机制，如果重试不成功，可人工介入
        redisTemplate.opsForValue().set(CACHE_PRODUCT_COMPOSITE_KEY, updatedItems);
        return (ProductComposite) updatedItems;
    }

    /**
     * 删除商品类目
     * @param item
     * @return
     */
    public ProductComposite delItems(ProductItem item) {
        // 1. 先更新数据库
        productItemRepository.delItem(item.getId());
        // 2. item --> productComposite
        ProductComposite addItem = ProductComposite.builder()
                .id(item.getId())
                .pid(item.getPid())
                .build();
        // 3. 通过addItemVisitor添加节点
        AbstractProductItem updatedItems = delItemVisitor.visitor(addItem);
        // 4. 更新redis
        // TODO 此处可以做重试机制，如果重试不成功，可人工介入
        redisTemplate.opsForValue().set(CACHE_PRODUCT_COMPOSITE_KEY, updatedItems);
        return (ProductComposite) updatedItems;
    }

    /**
     * 查询全部商品类目信息
     * @return
     */
    public ProductComposite fetchAllItems() {
        // 1. 先去查redis
        Object cacheItem = redisTemplate.opsForValue().get(CACHE_PRODUCT_COMPOSITE_KEY);
        // 2. 有数据直接返回
        if (cacheItem != null) {
            return (ProductComposite) cacheItem;
        }
        // 3. 没有数据去查数据库
        List<ProductItem> fetchDbItems = productItemRepository.findAll();
        if (CollectionUtils.isEmpty(fetchDbItems)) {
            throw new UnsupportedOperationException("Product items should not be empty in DB!");
        }
        // 4. 将数据拼接成树形结构
        ProductComposite items = generateProductTree(fetchDbItems);
        // 5. 写入redis
        redisTemplate.opsForValue().set(CACHE_PRODUCT_COMPOSITE_KEY, items);
        // 6. 返回
        return items;
    }

    /**
     * 将数据库List接口的ProductItem数据转换成ProductComposite的树形结构数据
     * @param fetchDbItems
     * @return
     */
    private ProductComposite generateProductTree(List<ProductItem> fetchDbItems) {
        List<ProductComposite> composites = new ArrayList<>(fetchDbItems.size());
        // TODO 这里这个BeanUtils copy一下不就行了？
        fetchDbItems.forEach(dbItem -> composites.add(
                ProductComposite.builder()
                        .id(dbItem.getId())
                        .name(dbItem.getName())
                        .pid(dbItem.getPid())
                        .build()
        ));
        // 将数据库的所有元素的pid进行分组
        Map<Integer, List<ProductComposite>> groupingList =
                composites.stream().collect(Collectors.groupingBy(ProductComposite::getPid));

        // 将item的子节点装入child中
        composites.forEach(item -> {
            List<ProductComposite> list = groupingList.get(item.getId());
             item.setChild(list == null ? new ArrayList<>() :
                     list.stream().map(x -> (AbstractProductItem) x).collect(Collectors.toList()));
        });
        // 返回哑节点
        ProductComposite composite = composites.size() == 0 ? null : composites.get(0);
        return composite;
    }
}
